Inside Macintosh: QuickTime Components

Previous | Chapter Top | Chapter Contents | Next

Decompressing an Image

When decompressing an image, the Image Compression Manager performs these three major tasks:

  1. The Image Compression Manager first determines which decompressor is best able to decompress the image. To do so, the Image Compression Manager examines the source image as well as the parameters specified by the application. If the application requested a specific decompressor, the Image Compression Manager uses that decompressor (unless it is not installed, in which case the Image Compression Manager returns an error to the application). If the application did not request a decompressor, the Image Compression Manager chooses the decompressor that will do the best job. The Image Compression Manager collects the information it needs to choose a decompressor by issuing the CDPreDecompress request to each qualifying decompressor (see CDPreDecompress for a detailed description of the CDPreDecompress function).
  2. If the chosen decompressor can handle the image directly, the Image Compression Manager passes the request through to the decompressor. The decompressor then processes the image and returns the image to the specified location.
  3. If none of the decompressors can handle all of the conditions (matrix mapping, masking or matting, depth conversion, and so on) the Image Compression Manager allocates an offscreen buffer and passes image bands to the decompressor at a depth that the decompressor can handle by issuing a CDBandDecompress request. (For details on the CDBandDecompress function, see CDBandDecompress ). The decompressor processes each band, building the image as it goes. When the image has been completely decompressed, the Image Compression Manager returns control to the application.

Choosing a Decompressor

Listing 3 provides an example of how a decompressor is chosen. The Image Compression Manager calls the CDPreDecompress function (described on CDPreDecompress ) before an image is decompressed. The decompressor returns information about how it can decompress an image. The Image Compression Manager can fit the destination pixel map to your decompressor's requirements if it is not able to support decompression to the destination directly. The capability information the decompressor returns includes

When your decompressor component is called with the CDPreDecompress function, it can handle all aspects of the call itself, or only the most common ones. All decompressors must handle at least one case.

This section contains a bulleted list of some of the operations your decompressor component can perform during the decompression operation. The list describes which parameters in the decompression parameters structure (described on The Decompression Parameters Structure ) indicate the operations are required and which flags in the flags field of the compressor capabilities structure (described on The Compressor Capability Structure ) must be set to allow your decompressor to handle them.

For sequences of images the conditionFlags field in the decompression parameters structure can be used to determine which parameters may have changed since the last decompression operation. These parameters are also indicated in the bulleted list.

Since your decompressor's capabilities depend on the full combination of parameters, it must inspect all the relevant parameters before indicating that it will perform one of the operations itself. For instance, if your decompressor has hardware that can perform scaling only if the destination pixel depth is 32 and there is no clipping, then the pre-decompression operation would have to check the following fields in the decompression parameters structure: the matrix field, the pixelSize field of the destination pixel map structure pointed to by the destPixMap field, and the maskBits fields. Only then could the decompressor decide whether to set the codecCanScale flag in the capabilities field of the decompression parameters structure.

Listing 3 Preparing for simple decompression

pascal long
CDPreDecompress(Handle storage, register CodecDecompressParams *p)
{
    register CodecCapabilities*capabilities = p->capabilities;
    RectdRect = p->srcRect;
    
    /*  
        Check if the matrix is OK for this decompressor.
        This decompressor doesn't do anything fancy.
    */
    
    if ( !TransformRect(p->matrix,&dRect,nil) )
        return(codecConditionErr);

    /*  
        Decide which depth compressed data this decompressor can
        deal with.
    */
    
    switch ( (*p->imageDescription)->depth ) {
        case 16:
            break;
        default:
            return(codecConditionErr);
            break;
    }
        /*
            This decompressor can deal only with 32-bit pixels.
        */

    capabilities->wantedPixelSize = 32;
    
    /*
        The smallest possible band the decompressor can handle is
        2 scan lines.
    */
    
    capabilities->bandMin = 2;

    /* This decompressor can deal with 2 scan line high bands. */

    capabilities->bandInc = 2;
    
    /*
        If this decompressor needed its pixels be aligned on
        some integer multiple, you would set extendWidth and
        extendHeight to the number of pixels by which you need the
        destination extended. If you don't have such requirements
        or if you take care of them yourself, you set extendWidth
        and extendHeight to 0.
    */

    capabilities->extendWidth = p->srcRect.right & 1;
    capabilities->extendHeight = p->srcRect.bottom & 1;
    
    return(noErr);
}

Decompressing a Horizontal Band of an Image

Listing 4 shows how to decompress the horizontal band of an image. The Image Compression Manager calls the CDBandDecompress function when it wants a decompressor to decompress an image or a horizontal band of an image. The pixel data indicated by the baseAddr field is guaranteed to conform to the criteria your decompressor specified in the CDPreDecompress function.

This example does not perform decompression on bands with a bit depth of more than one or an extension of width and height. If the example did do so, it would handle these cases faster.

Listing 4 Performing a decompression operation

pascal long
CDBandDecompress(Handle storage,register CodecDecompressParams *p)
{
    Rect                dRect;
    long                offsetH,offsetV;
    Globals             **glob = (Globals **)storage;
    long                numLines,numStrips;
    short               rowBytes;
    long                stripBytes;
    short               width;
    register short      y;
    register char*      baseAddr;
    char                *cDataPtr;
    char                mmuMode = 1;
    OSErr               result = noErr;
    /*
        Calculate the real base address based on the boundary
        rectangle. If it's not a linear transformation, this
        decompressor does not perform the operation.
*/

    dRect = p->srcRect;
    if ( !TransformRect(p->matrix,&dRect,nil) )
        return(paramErr);

    /*  If there is a progress function, give it an open call at
        the start of this band.
    */

    if (p->progressProcRecord.progressProc)
        p->progressProcRecord.progressProc(codecProgressOpen,0,
            p->progressProcRecord.progressRefCon);
    
    /*
        Initialize some local variables.
    */
    
    width = (*p->imageDescription)->width;
    rowBytes = p->dstPixMap.rowBytes;
    numLines = p->stopLine - p->startLine;              /* number of scan lines
                                                            in this band */
    numStrips = (numLines+1)>>1;                        /* number of strips in
                                                            this band */
    stripBytes = ((width+1)>>1) * 5;                    /* number of bytes in
                                                            1 strip of blocks */

    cDataPtr = p->data;
    
    /*
        Adjust the destination base address to be at the beginning
        of the desired rectangle.
    */
    
    offsetH = (dRect.left - p->dstPixMap.bounds.left);
    switch ( p->dstPixMap.pixelSize ) {
        case 32:
            offsetH <<=2;       /* 1 pixel = 4 bytes */
            break;
        case 16:
            offsetH <<=1;       /* 1 pixel = 2 bytes */
            break;
        case 8:                         
            break;                      /* 1 pixel = 1 byte */
        default:
            result = codecErr;          /* This decompressor doesn't handle
                                            these cases, although it
                                            could. */
        goto bail;
    }
    offsetV = (dRect.top - p->dstPixMap.bounds.top) * rowBytes;
    baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
    /*
        If your decompressor component is skipping some data,
        it just skips it here. You can tell because
        firstBandInFrame indicates this is the first band for a new
        frame, and if startLine is not 0, then that many lines were
        clipped out.
     */
    if ( (p->conditionFlags & codecConditionFirstBand) &&
            p->startLine != 0 ) {
        if ( p->dataProcRecord.dataProc ) {
            for ( y=0; y < p->startLine>>1; y++ ) {
                if ( (result=p->dataProcRecord.dataProc
                         (&cDataPtr,stripBytes,
                        p->dataProcRecord.dataRefCon)) != noErr ) {
                    result = codecSpoolErr;
                    goto bail;
                }
                cDataPtr += stripBytes;
            }
        } else
            cDataPtr += (p->startLine>>1) * stripBytes;
    }
/*
    If there is a data-loading function spooling the data to your
    decompressor, then you have to decompress the data in the
    chunk size that is specified, or, if there is a progress
    function, you must make sure to call it as you go along.
*/
    if ( p->dataProcRecord.dataProc ||
         p->progressProcRecord.progressProc ) {

        SharedGlobals *sg = (*glob)->sharedGlob;
    
        for (y=0; y < numStrips; y++) {
            if (p->dataProcRecord.dataProc) {
                if ( (result=p->dataProcRecord.dataProc
                         (&cDataPtr,stripBytes,
                        p->dataProcRecord.dataRefCon)) != noErr ) {
                    result = codecSpoolErr;
                    goto bail;
                }
            }
            SwapMMUMode(&mmuMode);
            DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
            SwapMMUMode(&mmuMode);
            baseAddr += rowBytes<<1;
            cDataPtr += stripBytes;
            if (p->progressProcRecord.progressProc) {
                if ( (result=p->progressProcRecord.progressProc
                        (codecProgressUpdatePercent,
                    FixDiv(y, numStrips),
                    p->progressProcRecord.progressRefCon)) != noErr ) {
                    result = codecAbortErr;
                     goto bail;
                }
            }
        }
    
/*
    Otherwise, do the fast case.
*/
    } else {
        SharedGlobals *sg = (*glob)->sharedGlob;
        shorttRowBytes = rowBytes<<1;
        
        SwapMMUMode(&mmuMode);
        for ( y=numStrips; y--; ) {
            DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
            baseAddr += tRowBytes;
            cDataPtr += stripBytes;
        }
        SwapMMUMode(&mmuMode);
    }
/*
    IMPORTANT-- Update the pointer to data in the decompression
    parameters structure, so that when your decompressor gets the
    next band, you'll be at the right place in your data.
*/

    p->data = cDataPtr;
    
    if ( p->conditionFlags & codecConditionLastBand ) {
        /*
            Tie up any loose ends on the last band of the frame.
        */
    }

bail:
    /*
        If there is a progress function, give it a close call
        at the end of this band.
    */

    if (p->progressProcRecord.progressProc)
        p->progressProcRecord.progressProc(codecProgressClose,0,
            p->progressProcRecord.progressRefCon);
    return(result);
}

© 1997 Apple Computer, Inc.

Previous | Chapter Top | Chapter Contents | Next